﻿using System;
using System.Collections;
using System.Collections.Generic;
using Castle.MicroKernel;
using Microsoft.Practices.ServiceLocation;

namespace MetaContainer.Castle
{
   /// <summary>
   /// Adapter for MetaContainer using Castle's MicroKernel as implementation of <see cref="IMetaContainer"/>.
   /// </summary>
   public sealed class CastleKernelMetaContainerAdapter : ServiceLocatorImplBase, IMetaContainer
   {
      private readonly IKernel _kernel;

      /// <summary>
      /// Creates new adapter instance for provided kernel.
      /// </summary>
      /// <param name="kernel">Kernel to be wrapped by <see cref="IMetaContainer"/>.</param>
      public CastleKernelMetaContainerAdapter(IKernel kernel)
      {
         _kernel = kernel;
         _kernel.Resolver.AddSubResolver(new FactoryMethodDependencyResolver());
      }

      #region IMetaContainer Members
      public void Register(Type from, Type to, string name, Scope scope)
      {
         name = name ?? Guid.NewGuid().ToString();
         _kernel.AddComponent(name, from, to, Common.MapLifestyle(scope));         
      }
      public void Register(Type from, Func<string, object> factoryMethod, Scope scope)
      {
         Common.RegisterInKernel(_kernel, from, factoryMethod, scope);
      }
      public IMetaContainer CreateChildContainer()
      {
         IKernel childKernel = new DefaultKernel();
         _kernel.AddChildKernel(childKernel);
         childKernel.Parent = _kernel;
         return new CastleKernelMetaContainerAdapter(childKernel);
      }
      #endregion

      #region ServiceLocatorImplBase Members
      protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
      {
         return (object[])_kernel.ResolveAll(serviceType, null);
      }
      protected override object DoGetInstance(Type serviceType, string key)
      {         
         if (key != null)
         {              
            if (!_kernel.HasComponent(key))
            {
               Hashtable args = new Hashtable();
               args["key"] = key;
               return _kernel.Resolve(serviceType, args);
            }
            return _kernel.Resolve(key, serviceType);
         }
         return _kernel.Resolve(serviceType);
      }
      #endregion
   }

   public class FactoryMethodDependencyResolver : ISubDependencyResolver
   {
      #region ISubDependencyResolver Members
      public bool CanResolve(CreationContext context, ISubDependencyResolver parentResolver, global::Castle.Core.ComponentModel model, global::Castle.Core.DependencyModel dependency)
      {         
         return parentResolver.CanResolve(context, null, model, dependency);
      }
      public object Resolve(CreationContext context, ISubDependencyResolver parentResolver, global::Castle.Core.ComponentModel model, global::Castle.Core.DependencyModel dependency)
      {
         return parentResolver.Resolve(context, null, model, dependency);
      }
      #endregion
   }
}
